<?php

require_once "AbstractCompositionObject.php";

Loader::using(array(
    'underscore::libs::MEF::CompositionInitializer',
    'underscore::libs::MEF::Container::Default',
    "underscore::core::exceptions::ApplicationNotFoundException"
));

/**
 * This class loads and holds all the modules of an application.
 *
 * @author JeyDotC
 */
class ModulesCollection extends AbstractCompositionObject {

    private static $instance;
    
    private $app = "";
    private $modules = array();
    private $defaultModule;
    private $allModulesMetadata = array();
    private $appPath = "";

    /**
     * @return ModulesCollection
     */
    public static function instance() {
        if(!isset (self::$instance)){
            self::$instance = new ModulesCollection();
        }
        
        return self::$instance;
    }
    
    private function __construct() {}

    /**
     *
     * {@inheritdoc} 
     */
    protected function getContainer() {
        return new MEF_Container_Default();
    }

    /**
     * Tells if the path to the application exists.
     * 
     * @param string $app The application name.
     * @return boolean <code>True</code> if the folder exists, <code>false</code> otherwise.
     */
    public function applicationExists($app) {
        $extists = false;

        if (!empty($app)) {
            $this->appPath = $this->calculateAppPath($app);
            $extists = is_dir($this->appPath);
        }

        return $extists;
    }

    /**
     * Loads all modules for an application.
     * 
     * @param string $app The name of the application.
     * 
     * @throws ApplicationNotFoundException If there is not such application.
     */
    public function loadApp($app) {

        if (!$this->applicationExists($app)) {
            throw new ApplicationNotFoundException($app, $this->appPath);
        }

        //Avoid loading the same app again!
        if ($this->app != $app) {
            $this->app = $app;
            $directories = new DirectoryHelper(new RecursiveDirectoryIterator($this->appPath));
            $modules = $directories->listFiles(2);

            foreach ($modules as $module) {
                //Avoid including .tpl or other kind of files.
                if (substr($module, -4) == ".php") {
                    require_once $module;
                }
            }

            $this->satisfyImports();
            $this->loadModulesMetadata();
            Module::setAllModulesMetadata($this->allModulesMetadata);
        }
    }

    /**
     * Says if there is a module in the modules list with the given name.
     * 
     * @param string $moduleName The name of the module.
     * @return boolean <code>True</code> Id the module has been loaded, <code>False</code> otherwise.
     */
    public function moduleIsLoaded($moduleName) {
        return array_key_exists($moduleName, $this->modules);
    }

    /**
     * Gets the module which corresponds to the given name.
     * 
     * @param string $moduleName The name of the module.
     * @return Module The module. 
     */
    public function get($moduleName) {
        return $this->modules[$moduleName];
    }

    /**
     * Gets the current App name.

     * @return string The current appName 
     */
    public function getCurrentApp() {
        return $this->app;
    }

    /**
     *
     * @param array $modules 
     * 
     * @import-many Modules
     */
    public function setModules(array $modules = array()) {
        $this->modules = $modules;
    }

    private function loadModulesMetadata() {
        foreach ($this->modules as $class => /* @var $module Module */$module) {
            $module->setAppName($this->app);
            $moduleMetadata = $this->getCompositionInitializer()->getMetadataForClass($class);
            $this->allModulesMetadata[$class] = $moduleMetadata;
        }
    }

    /**
     * Says if there is any default module loaded.
     * 
     * @return boolean If whether or not the dafault module is loaded.
     */
    public function defaultModuleIsLoaded() {
        return $this->defaultModule != null;
    }

    /**
     * Gets the dafault module.
     * 
     * @return Module
     */
    public function getDefaultModule() {
        return $this->defaultModule;
    }

    /**
     *
     * @param Module $defaultModule 
     * 
     * @import Modules
     * @import-metadata default-module
     */
    public function setDefaultModule(Module $defaultModule) {
        $this->defaultModule = $defaultModule;
        $this->defaultModule->setAppName($this->app);
    }

    private function calculateAppPath($app) {
        $path = "";
        $root = $this->getBusinesRoot();

        if (!empty($root)) {
            $path = "$root::$app::";
        } else {
            $path = "$app::";
        }

        return Loader::toSinglePath($path, "");
    }

    private function getBusinesRoot() {
        return ModulesContext::instance()->getConfig()->get("BusinessRoot");
    }

}

?>
